package la.iok.hzsvn.lewin.movie.shopmovie.service;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import la.iok.hzsvn.lewin.movie.core.RoleEnum;
import la.iok.hzsvn.lewin.movie.core.Token;
import la.iok.hzsvn.lewin.movie.core.exception.ErrorCode;
import la.iok.hzsvn.lewin.movie.core.model.*;
import la.iok.hzsvn.lewin.movie.core.movieservice.vo.MovieLevelVo;
import la.iok.hzsvn.lewin.movie.core.shopmovie.param.CouponBaseFilterParam;
import la.iok.hzsvn.lewin.movie.core.shopmovie.param.CouponListParam;
import la.iok.hzsvn.lewin.movie.core.shopmovie.param.CouponParam;
import la.iok.hzsvn.lewin.movie.core.shopmovie.vo.CouponVo;
import la.iok.hzsvn.lewin.movie.services.MovieServiceInvoker;
import la.iok.hzsvn.lewin.movie.services.OrgUnitIntegration;
import la.iok.hzsvn.lewin.movie.shopmovie.cfg.RedisKey;
import la.iok.hzsvn.lewin.movie.shopmovie.entity.Coupon;
import la.iok.hzsvn.lewin.movie.shopmovie.entity.CouponDefinition;
import la.iok.hzsvn.lewin.movie.shopmovie.mapper.CouponMapper;
import la.iok.hzsvn.lewin.movie.shopmovie.model.CouponQRInfo;
import la.iok.hzsvn.lewin.movie.shopmovie.model.EfficacyCouponVo;
import la.iok.hzsvn.lewin.mybatis.model.PageParam;
import la.iok.hzsvn.lewin.mybatis.model.PageVo;
import la.iok.hzsvn.lewin.mybatis.model.SortParam;
import la.iok.hzsvn.lewin.mybatis.service.BaseServiceImpl;
import la.iok.hzsvn.redis.service.RedisService;
import la.iok.hzsvn.share.annotations.NotNull;
import la.iok.hzsvn.share.annotations.NotNullElement;
import la.iok.hzsvn.share.annotations.Nullable;
import la.iok.hzsvn.share.collection.IdOperators;
import la.iok.hzsvn.share.phone.PhoneNumberUtils;
import la.iok.hzsvn.share.tools.datetime.DateTimeUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.swing.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CouponServiceImpl extends BaseServiceImpl<Coupon, CouponMapper, Long> implements CouponService {

    private final CouponDefinitionService couponDefinitionService;
    private final MovieServiceInvoker movieServiceInvoker;
    private final OrgUnitIntegration orgUnitIntegration;
    private final RedisService redisService;
    private final RedisKey redisKey;

    public CouponServiceImpl(CouponMapper mapper,
                             CouponDefinitionService couponDefinitionService,
                             MovieServiceInvoker movieServiceInvoker,
                             OrgUnitIntegration orgUnitIntegration,
                             RedisService redisService,
                             RedisKey redisKey) {
        super(mapper);
        this.couponDefinitionService = couponDefinitionService;
        this.movieServiceInvoker = movieServiceInvoker;
        this.orgUnitIntegration = orgUnitIntegration;
        this.redisService = redisService;
        this.redisKey = redisKey;
    }

    @Override
    public Long grant(@NotNull Token token, @Nullable Long userId, @NotNull CouponParam param) {
        CouponDefinition def = validateCouponDef(token, param.getCouponDefinitionId());
        UserVo user = movieServiceInvoker.userInfo(userId);
        ErrorCode.INVALID_USER.assertNotNull(user);
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(user.normalStatus(),"状态","用户状态异常");
        Coupon entity = doSave(token, def, user, now(), endTime(param));
        return entity.getId();
    }

    @Override
    public CouponQRInfo create(@NotNull Token token, @NotNull CouponParam param) {
        CouponDefinition def = validateCouponDef(token, param.getCouponDefinitionId());
        Coupon entity = doSave(token, def, null,now(),endTime(param));
        Calendar c = Calendar.getInstance();
        c.add(Calendar.MONTH,1);
        CouponQRInfo info = QRCodeBaseInfo.generateQrCodeInfo(CouponQRInfo.class, c.getTime());
        info.setUuid(qrcodeId());
        info.setId(entity.getId());
        info.setAmount(entity.getAmount());
        info.setName(entity.getName());
        info.setType(entity.getType());
        String key = redisKey.couponQrKey(info.getUuid());
        redisService.set(key, entity.getId(),30, TimeUnit.DAYS);
        return info;
    }

    @Override
    public String qrcodeId(){
        int timestamp = (int) ((System.currentTimeMillis() / 1000) & 0x0FFF);
        String key = redisKey.redisIncrementIdKey(timestamp);
        Long serial = redisService.increment(key);
        redisService.expire(key, 10, TimeUnit.MINUTES);   // 10分钟后过期，实际上1秒后就不需要了
        Long id = timestamp << 19 | serial;  //低19位为id,之后12位时间戳，最高位0
        return String.format("%08X",id);
    }

    @Override
    public void take(Token token, String uuid) {
        String key = redisKey.couponQrKey(uuid);
        Long id = (Long) redisService.get(key);
        ErrorCode.QR_CODE_EXPIRED.assertNotNull(id, key);
        Coupon entity = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(entity, id);
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(entity.getUserId()==null||entity.getUserId() <= 0,"状态","优惠券已经被领取");
        entity.setUserId(token.getUserId());
        entity.setStatus(entity.STATUS_UNUSED);
        ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(1,mapper.updateUser(entity));

    }

    @Override
    public PageVo<CouponVo> page(@NotNull Token token, @NotNull PageParam pageParam, @NotNull SortParam sortParam, @NotNull CouponListParam listParam) {
        ErrorCode.INVALID_USER.assertNotNull(token.getUserId());
        Set<Long> orgUnitIds = new HashSet<>();
        Long userId = null;
        if(!token.hasAnyAuthority(RoleEnum.Admin,RoleEnum.Manager,RoleEnum.ShopManager,RoleEnum.CarpoolMaster,RoleEnum.CustomerService))
        {
            userId = token.getUserId();
            listParam.setUserId(userId);
            listParam.setUserType(2);
        }
        /*
        if(!token.hasAuthority(RoleEnum.Admin)){
            userId = token.getUserId();
            orgUnitIntegration.userManageOrgUnits(token, orgUnitIds);
        }
         */
        Page<Coupon> page = PageHelper.startPage(pageParam.getPage(), pageParam.getLimit());
        mapper.list(sortParam.orderColumn("orgUnitId"),sortParam.order(),listParam, orgUnitIds,userId);
        return page(page, po2vo(page));
    }

    private CouponVo po2vo(Coupon coupon, List<SimpleUser> users, List<SimpleOrgUnitVo> orgUnits, List<MovieLevelVo> movieLevels) {
        CouponVo vo = new CouponVo();
        BeanUtils.copyProperties(coupon,vo);
        SimpleUser user = IdOperators.find(users, coupon.getUserId());
        if(user != null){
            vo.setUserNickname(user.getNickname());
            vo.setUserPhoneNumber(PhoneNumberUtils.encPhoneNumber(user.getPhoneNumber()));
        }
        user = IdOperators.find(users, coupon.getCreateUserId());
        if(user != null){
            vo.setCreateUserNickname(user.getNickname());
            vo.setCreateUserPhoneNumber(PhoneNumberUtils.encPhoneNumber(user.getPhoneNumber()));
        }
        SimpleOrgUnitVo orgUnit = IdOperators.find(orgUnits, coupon.getOrgUnitId());
        if(orgUnit != null){
            vo.setOrgUnitName(orgUnit.getName());
        }
        MovieLevelVo level = IdOperators.find(movieLevels, coupon.getMovieLevelId());
        if(level != null){
            vo.setMovieLevelName(level.getName());
        }else if(Objects.equals(coupon.getMovieLevelId(), -1L)){
            vo.setMovieLevelName("无限制");
        }
        return vo;
    }

    private List<CouponVo> po2vo(@NotNull @NotNullElement List<Coupon> coupons){
        Set<Long> userIds = coupons.stream().map(Coupon::getUserId).collect(Collectors.toSet());
        userIds.addAll(coupons.stream().map(Coupon::getCreateUserId).collect(Collectors.toSet()));
        List<SimpleUser> users = movieServiceInvoker.userInfo(userIds);
        Set<Long> orgs = coupons.stream().map(Coupon::getOrgUnitId).collect(Collectors.toSet());
        List<SimpleOrgUnitVo> orgUnits = movieServiceInvoker.orgUnits(orgs);
        List<MovieLevelVo> movieLevels = movieServiceInvoker.movieLevels();
        return coupons.stream().map(coupon -> po2vo(coupon, users, orgUnits, movieLevels)).collect(Collectors.toList());
    }

    @Override
    public int use(@NotNull Coupon coupon) {
        return mapper.use(coupon);
    }

    @Override
    public int waitLock(@NotNull Coupon coupon) {
        return mapper.waitLock(coupon);
    }

    @Override
    public int unWaitLock(@NotNull Coupon coupon) {
        return mapper.unWaitLock(coupon);
    }

    @Override
    public int lock(Coupon coupon) {
        return mapper.lock(coupon);
    }

    public int unLock(Coupon coupon) {
        return mapper.unLock(coupon);
    }


    @Override
    public void delete(Token token, Long id) {
        token.validAnyAuthority(RoleEnum.Manager, RoleEnum.ShopManager);
        Coupon entity = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(entity, id);
        ErrorCode.PROPERTY_CHECK_ERROR.assertFalse(entity.statusUsed(),"状态","只能删除未使用的优惠券");
        ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(entity.getOrgUnitId(), token.getOrgUnitId(), entity.getOrgUnitId());
        delete(id);
    }

    @Override
    public CouponVo select(@NotNull Token token, @NotNull Long id) {
        Coupon entity = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(entity, id);
        List<SimpleUser> users = movieServiceInvoker.userInfo(Arrays.asList(entity.getCreateUserId(), entity.getUserId()));
        SimpleOrgUnitVo orgUnit = movieServiceInvoker.orgUnitInfo(entity.getOrgUnitId());
        return po2vo(entity, users, Collections.singletonList(orgUnit),movieServiceInvoker.movieLevels());
    }

    @Nullable
    @Override
    public CouponVo select(@NotNull Token token, @NotNull String uuid) {
        String key = redisKey.couponQrKey(uuid);
        Long id = (Long) redisService.get(key);
        ErrorCode.QR_CODE_EXPIRED.assertNotNull(id, key);
        return select(token, id);
    }

    @Override
    public void deleteDefinition(Token token, Long couponDefId) {
        token.validAnyAuthority(RoleEnum.Admin, RoleEnum.Manager, RoleEnum.ShopManager);
        List<Coupon> list = listByProperty("couponDefinitionId", couponDefId);
        ErrorCode.PROPERTY_CHECK_ERROR.assertEmpty(list, "优惠券", "存在关联的优惠券不能删除");
        couponDefinitionService.delete(token, couponDefId);
    }

    @NotNull
    @NotNullElement
    @Override
    public List<CouponVo> usable(@NotNull Token token, @NotNull CouponBaseFilterParam param) {
        List<Coupon> list = mapper.usable(token.getUserId(), param);
        return po2vo(list);
    }

    @Override
    public PageVo<EfficacyCouponVo> efficacypage(@NotNull Token token, @NotNull PageParam pageParam, @NotNull SortParam sortParam,@NotNull Long dayNum) {
        ErrorCode.INVALID_USER.assertNotNull(token.getUserId());
     //   Page<Coupon> page = PageHelper.startPage(pageParam.getPage(), pageParam.getLimit());
        List<Coupon> efficacyCouponList=mapper.efficacyTiming(sortParam.orderColumn("orgUnitId"),sortParam.order(),token.getOrgUnitId(),dayNum);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(efficacyCouponList);
        Map<Long,EfficacyCouponVo> voMap=new HashMap<>();
        for(int i=0;i<efficacyCouponList.size();i++)
        {
            Coupon coupon=efficacyCouponList.get(i);
            if(voMap.containsKey(coupon.getUserId()))
            {
                System.out.println("筛选出存在项"+"\n");
                EfficacyCouponVo vo=voMap.get(coupon.getUserId());
                vo.addCoupon(coupon);
                voMap.put(coupon.getUserId(),vo);
            }else {
                System.out.println("筛选出新用户数据"+"\n");
                UserVo userVo=movieServiceInvoker.userInfo(coupon.getUserId());
                ErrorCode.RECORD_NOT_EXIST.assertNotNull(userVo);

                EfficacyCouponVo vo=new EfficacyCouponVo(userVo);
                vo.addCoupon(coupon);
                voMap.put(coupon.getUserId(),vo);
            }
        }
        Page<EfficacyCouponVo> page = PageHelper.startPage(pageParam.getPage(), pageParam.getLimit());
        List<EfficacyCouponVo> pagelist = new ArrayList<>(voMap.values());
        System.out.println("打印List数据对象数量："+pagelist.size());
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(pagelist);
        return page(page,pagelist);

    }


    private Date now(){
        Calendar c = Calendar.getInstance();
        c.set(Calendar.MILLISECOND,0);
        return c.getTime();
    }

    private Date endTime(@NotNull CouponParam param){
        if(param.getEndTime() != null){
            //如果指定了结束时间,那直接使用
            return DateTimeUtil.setMilli(param.getEndTime(),0);
        }
        Integer seconds = param.getSeconds();
        if(seconds == null || seconds == 0 || seconds == -1 || seconds < -6){
            seconds = -4;  //默认本月内有效
        }
        if(seconds < 0){
            if(seconds == -1){
                return new Date(Long.MAX_VALUE);
            }else if(seconds == -2){
                return DateTimeUtil.dayEnd(Calendar.getInstance(),0);
            }else if(seconds == -3){
                return DateTimeUtil.dayEnd(DateTimeUtil.weekEnd(new Date()),0);
            }else if(seconds == -4){
                return DateTimeUtil.dayEnd(DateTimeUtil.monthEnd(new Date()),0);
            }else if(seconds == -5){
                return DateTimeUtil.dayEnd(DateTimeUtil.seasonEnd(new Date()),0);
            }else {
                return DateTimeUtil.dayEnd(DateTimeUtil.yearEnd(new Date()),0);
            }
        }else{
            Calendar c = Calendar.getInstance();
            c.set(Calendar.MILLISECOND,0);
            c.add(Calendar.SECOND, seconds);
            return c.getTime();
        }
    }

    private Coupon doSave(Token token, CouponDefinition def, @Nullable SimpleUser user, @Nullable Date start, @Nullable Date end) {
        Coupon entity = new Coupon();
        entity.setCreateUserId(token.getUserId());
        entity.setCouponDefinitionId(def.getId());
        entity.setName(def.getName());
        entity.setType(def.getType());
        entity.setAmount(def.getAmount());
        entity.setInitAmount(def.getAmount());
        entity.setMovieLevelId(def.getMovieLevelId());
        if(def.typePercent()){
            entity.setMaxAmount(def.getMaxAmount());
        }
        entity.setOrgUnitId(token.getOrgUnitId());
        entity.setEnd(end);
        entity.setStart(start);
        if(user!=null){
            entity.setUserId(user.getId());
            entity.setStatus(Coupon.STATUS_UNUSED);
        }
        insertSelective(entity);
        return entity;
    }

    private CouponDefinition validateCouponDef(Token token, Long couponDefinitionId) {
        token.validAnyAuthority(RoleEnum.Manager, RoleEnum.ShopManager,RoleEnum.CarpoolMaster,RoleEnum.CustomerService);
        CouponDefinition def = couponDefinitionService.select(couponDefinitionId);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(def, couponDefinitionId);
        if(!def.systemDefined()){
            ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(def.getOrgUnitId(), token.getOrgUnitId(), def.getOrgUnitId());
        }
        return def;
    }
}
